package cn.achuan.spring.springcloud.demo;

import cn.achuan.spring.springcloud.demo.product.Product;
import cn.achuan.spring.springcloud.demo.product.ProductMsg;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.stream.messaging.Source;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

/**
 * 商品服务。
 * 当商品配置变更时，如：修改、删除等，就需要构建一个商品消息，然后就可以将该消息
 * 通过Kafka发送给相应监听的微服务进行处理。
 */
@Service
public class ProductService {
    private static final Logger logger = LoggerFactory.getLogger(ProductService.class);

    /*
     * Spring Cloud Stream提供的一个抽象消息发送接口，通过该接口中的output()就
     * 可以获取一个消息发送通道，然后就可以往该通道中send()消息了。
     */
    private Source source;

    private List<Product> productList;

    @Autowired
    public ProductService(Source source) {
        this.source = source;
        this.productList = this.buildProducts();
    }

    /**
     * 获取商品列表
     * @return
     */
    public List<Product> findAll() {
        return this.productList;
    }

    /**
     * 根据ItemCode获取
     * @param itemCode
     * @return
     */
    public Product findOne(String itemCode) {
        for (Product product : this.productList) {
            if (product.getItemCode().equalsIgnoreCase(itemCode))
                return product;
        }

        return null;
    }

    /**
     * 保存或更新商品信息
     * @param product
     * @return
     */
    public Product save(Product product) {
        // 实现商品保存处理
        for (Product sourceProduct : this.productList) {
            if (sourceProduct.getItemCode().equalsIgnoreCase(product.getItemCode())) {
                sourceProduct.setName(sourceProduct.getName() + "-new");
                sourceProduct.setPrice(sourceProduct.getPrice() + 100);
                product = sourceProduct;
                break;
            }
        }

        // sendMsg 和 fireEvent 实现的功能是一样的。
        // sendMsg 是直接使用Spring Cloud Stream发送消息。
        // fireEvent 是使用Spring Cloud Bus发布一个事件。
        // Stream通过对消息中间件进行抽象封装，提供一个统一的接口供我们发送和监听消息，而Bus则是在Stream基础之
        // 上再次进行抽象封装，使得我们可以在不用理解消息发送、监听等概念的基础上使用消息来完成业务逻辑的处理。

        // 发送商品消息
        //sendMsg(ProductMsg.P_UPDATE, product.getItemCode());

        // 发布商品消息
        fireEvent(ProductEvent.P_UPDATE, product);

        return product;
    }

    /**
     * 具体消息发送的实现
     * @param msgAction 消息类型
     * @param itemCode 商品货号
     */
    private void sendMsg(String msgAction, String itemCode) {
        ProductMsg productMsg = new ProductMsg(msgAction, itemCode);
        logger.debug("发送商品消息:{} ", productMsg);

        // 发送消息
        source.output().send(MessageBuilder.withPayload(productMsg).build());
    }

    /**
     * 发布商品事件
     * @param eventAction
     * @param product
     */
    protected void fireEvent(String eventAction, Product product) {
        ProductEvent productEvent = new ProductEvent(
                product,
                ApplicationContextHolder.getApplicationContext().getId(),
                "*:**",
                eventAction,
                product.getItemCode());

        // 发布事件
        RemoteApplicationEventPublisher.publishEvent(productEvent);
    }

    private List<Product> buildProducts() {
        List<Product> products = new ArrayList<>();
        products.add(new Product("item-1", "测试商品-1", "BandOne", 100));
        products.add(new Product("item-2", "测试商品-2", "BandOne", 200));
        products.add(new Product("item-3", "测试商品-3", "BandOne", 300));
        products.add(new Product("item-4", "测试商品-4", "BandOne", 400));
        products.add(new Product("item-5", "测试商品-5", "BandOne", 500));
        products.add(new Product("item-6", "测试商品-6", "BandOne", 600));
        return products;
    }
}